package repository

import (
	"fitness/go-admin/internal/model"
	"time"

	"gorm.io/gorm"
)

// ==================== 卡类型仓储 ====================

type CardTypeRepository interface {
	Create(cardType *model.MembershipCardType) error
	Update(cardType *model.MembershipCardType) error
	Delete(id uint) error
	GetByID(id uint) (*model.MembershipCardType, error)
	List(cardType *model.CardType, isActive *bool, page, pageSize int) ([]model.MembershipCardType, int64, error)
	GetActiveTypes() ([]model.MembershipCardType, error)
}

type cardTypeRepository struct {
	db *gorm.DB
}

func NewCardTypeRepository(db *gorm.DB) CardTypeRepository {
	return &cardTypeRepository{db: db}
}

func (r *cardTypeRepository) Create(cardType *model.MembershipCardType) error {
	return r.db.Create(cardType).Error
}

func (r *cardTypeRepository) Update(cardType *model.MembershipCardType) error {
	return r.db.Save(cardType).Error
}

func (r *cardTypeRepository) Delete(id uint) error {
	return r.db.Delete(&model.MembershipCardType{}, id).Error
}

func (r *cardTypeRepository) GetByID(id uint) (*model.MembershipCardType, error) {
	var cardType model.MembershipCardType
	err := r.db.First(&cardType, id).Error
	return &cardType, err
}

func (r *cardTypeRepository) List(cardType *model.CardType, isActive *bool, page, pageSize int) ([]model.MembershipCardType, int64, error) {
	var cardTypes []model.MembershipCardType
	var total int64

	query := r.db.Model(&model.MembershipCardType{})

	// 条件筛选
	if cardType != nil {
		query = query.Where("type = ?", *cardType)
	}
	if isActive != nil {
		query = query.Where("is_active = ?", *isActive)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&cardTypes).Error
	return cardTypes, total, err
}

func (r *cardTypeRepository) GetActiveTypes() ([]model.MembershipCardType, error) {
	var cardTypes []model.MembershipCardType
	err := r.db.Where("is_active = ?", true).Order("type, price").Find(&cardTypes).Error
	return cardTypes, err
}

// ==================== 会员卡仓储 ====================

type MembershipCardRepository interface {
	Create(card *model.MembershipCard) error
	Update(card *model.MembershipCard) error
	Delete(id uint) error
	GetByID(id uint) (*model.MembershipCard, error)
	GetByIDWithRelations(id uint) (*model.MembershipCard, error)
	GetByCardNo(cardNo string) (*model.MembershipCard, error)
	GetByMemberID(memberID uint) ([]model.MembershipCard, error)
	List(memberID *uint, cardNo string, status *model.CardStatus, expireSoon int, page, pageSize int) ([]model.MembershipCard, int64, error)
	GetExpiringSoonCards(days int) ([]model.MembershipCard, error)
	UpdateBalance(cardID uint, balance float64) error
	UpdateRemainingTimes(cardID uint, times int) error
	// 新增方法
	GetActiveCardByMemberID(memberID uint) (*model.MembershipCard, error)
	GetCardIDsByMemberID(memberID uint) ([]uint, error)
}

type membershipCardRepository struct {
	db *gorm.DB
}

func NewMembershipCardRepository(db *gorm.DB) MembershipCardRepository {
	return &membershipCardRepository{db: db}
}

func (r *membershipCardRepository) Create(card *model.MembershipCard) error {
	return r.db.Create(card).Error
}

func (r *membershipCardRepository) Update(card *model.MembershipCard) error {
	return r.db.Save(card).Error
}

func (r *membershipCardRepository) Delete(id uint) error {
	return r.db.Delete(&model.MembershipCard{}, id).Error
}

func (r *membershipCardRepository) GetByID(id uint) (*model.MembershipCard, error) {
	var card model.MembershipCard
	err := r.db.First(&card, id).Error
	return &card, err
}

func (r *membershipCardRepository) GetByIDWithRelations(id uint) (*model.MembershipCard, error) {
	var card model.MembershipCard
	err := r.db.Preload("Member").Preload("CardType").First(&card, id).Error
	return &card, err
}

func (r *membershipCardRepository) GetByCardNo(cardNo string) (*model.MembershipCard, error) {
	var card model.MembershipCard
	err := r.db.Where("card_no = ?", cardNo).First(&card).Error
	return &card, err
}

func (r *membershipCardRepository) GetByMemberID(memberID uint) ([]model.MembershipCard, error) {
	var cards []model.MembershipCard
	err := r.db.Where("member_id = ?", memberID).Preload("CardType").Find(&cards).Error
	return cards, err
}

func (r *membershipCardRepository) List(memberID *uint, cardNo string, status *model.CardStatus, expireSoon int, page, pageSize int) ([]model.MembershipCard, int64, error) {
	var cards []model.MembershipCard
	var total int64

	query := r.db.Model(&model.MembershipCard{}).Preload("Member").Preload("CardType")

	// 条件筛选
	if memberID != nil {
		query = query.Where("member_id = ?", *memberID)
	}
	if cardNo != "" {
		query = query.Where("card_no LIKE ?", "%"+cardNo+"%")
	}
	if status != nil {
		query = query.Where("status = ?", *status)
	}
	if expireSoon > 0 {
		expireDate := time.Now().AddDate(0, 0, expireSoon)
		query = query.Where("expire_date <= ? AND expire_date > ?", expireDate, time.Now())
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&cards).Error
	return cards, total, err
}

func (r *membershipCardRepository) GetExpiringSoonCards(days int) ([]model.MembershipCard, error) {
	var cards []model.MembershipCard
	expireDate := time.Now().AddDate(0, 0, days)
	err := r.db.Where("status = ? AND expire_date <= ? AND expire_date > ?",
		model.CardStatusActive, expireDate, time.Now()).
		Preload("Member").Find(&cards).Error
	return cards, err
}

func (r *membershipCardRepository) UpdateBalance(cardID uint, balance float64) error {
	return r.db.Model(&model.MembershipCard{}).Where("id = ?", cardID).Update("balance", balance).Error
}

func (r *membershipCardRepository) UpdateRemainingTimes(cardID uint, times int) error {
	return r.db.Model(&model.MembershipCard{}).Where("id = ?", cardID).Update("remaining_times", times).Error
}

func (r *membershipCardRepository) GetActiveCardByMemberID(memberID uint) (*model.MembershipCard, error) {
	var card model.MembershipCard
	err := r.db.Where("member_id = ? AND status = ?", memberID, model.CardStatusActive).
		Preload("Member").Preload("CardType").First(&card).Error
	return &card, err
}

func (r *membershipCardRepository) GetCardIDsByMemberID(memberID uint) ([]uint, error) {
	var cards []model.MembershipCard
	err := r.db.Where("member_id = ?", memberID).Find(&cards).Error
	if err != nil {
		return nil, err
	}

	var cardIDs []uint
	for _, card := range cards {
		cardIDs = append(cardIDs, card.ID)
	}
	return cardIDs, nil
}

// ==================== 充值记录仓储 ====================

type RechargeRecordRepository interface {
	Create(record *model.RechargeRecord) error
	Update(record *model.RechargeRecord) error
	GetByID(id uint) (*model.RechargeRecord, error)
	GetByOrderNo(orderNo string) (*model.RechargeRecord, error)
	GetByCardID(cardID uint, page, pageSize int) ([]model.RechargeRecord, int64, error)
	GetByMemberID(memberID uint, startDate, endDate *time.Time, page, pageSize int) ([]model.RechargeRecord, int64, error)
	GetRecentByCardID(cardID uint, limit int) ([]model.RechargeRecord, error)
	GetTotalAmount(cardID uint, startDate, endDate *time.Time) (float64, error)
	GetRecentRecords(cardID uint, limit int) ([]model.RechargeRecord, error)
	GetTransactionsByCardIDs(cardIDs []uint, page, pageSize int) ([]model.RechargeRecord, int64, error)
}

type rechargeRecordRepository struct {
	db *gorm.DB
}

func NewRechargeRecordRepository(db *gorm.DB) RechargeRecordRepository {
	return &rechargeRecordRepository{db: db}
}

func (r *rechargeRecordRepository) Create(record *model.RechargeRecord) error {
	return r.db.Create(record).Error
}

func (r *rechargeRecordRepository) Update(record *model.RechargeRecord) error {
	return r.db.Save(record).Error
}

func (r *rechargeRecordRepository) GetByID(id uint) (*model.RechargeRecord, error) {
	var record model.RechargeRecord
	err := r.db.First(&record, id).Error
	return &record, err
}

func (r *rechargeRecordRepository) GetByOrderNo(orderNo string) (*model.RechargeRecord, error) {
	var record model.RechargeRecord
	err := r.db.Where("order_no = ?", orderNo).First(&record).Error
	return &record, err
}

func (r *rechargeRecordRepository) GetByCardID(cardID uint, page, pageSize int) ([]model.RechargeRecord, int64, error) {
	var records []model.RechargeRecord
	var total int64

	query := r.db.Model(&model.RechargeRecord{}).Where("card_id = ?", cardID).Preload("Operator")

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

func (r *rechargeRecordRepository) GetByMemberID(memberID uint, startDate, endDate *time.Time, page, pageSize int) ([]model.RechargeRecord, int64, error) {
	var records []model.RechargeRecord
	var total int64

	query := r.db.Model(&model.RechargeRecord{}).Where("member_id = ?", memberID)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

func (r *rechargeRecordRepository) GetRecentByCardID(cardID uint, limit int) ([]model.RechargeRecord, error) {
	var records []model.RechargeRecord
	err := r.db.Where("card_id = ? AND payment_status = ?", cardID, model.PaymentStatusSuccess).
		Order("created_at DESC").Limit(limit).Find(&records).Error
	return records, err
}

func (r *rechargeRecordRepository) GetTotalAmount(cardID uint, startDate, endDate *time.Time) (float64, error) {
	var total float64
	query := r.db.Model(&model.RechargeRecord{}).
		Where("card_id = ? AND payment_status = ?", cardID, model.PaymentStatusSuccess)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	err := query.Select("COALESCE(SUM(actual_amount), 0)").Scan(&total).Error
	return total, err
}

func (r *rechargeRecordRepository) GetRecentRecords(cardID uint, limit int) ([]model.RechargeRecord, error) {
	var records []model.RechargeRecord
	err := r.db.Where("card_id = ? AND payment_status = ?", cardID, model.PaymentStatusSuccess).
		Order("created_at DESC").Limit(limit).Find(&records).Error
	return records, err
}

func (r *rechargeRecordRepository) GetTransactionsByCardIDs(cardIDs []uint, page, pageSize int) ([]model.RechargeRecord, int64, error) {
	var records []model.RechargeRecord
	var total int64

	query := r.db.Model(&model.RechargeRecord{}).
		Where("card_id IN ? AND payment_status = ?", cardIDs, model.PaymentStatusSuccess).
		Preload("Operator")

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

// ==================== 消费记录仓储 ====================

type ConsumptionRecordRepository interface {
	Create(record *model.ConsumptionRecord) error
	GetByID(id uint) (*model.ConsumptionRecord, error)
	GetByRecordNo(recordNo string) (*model.ConsumptionRecord, error)
	GetByCardID(cardID uint, consumeType *model.ConsumeType, page, pageSize int) ([]model.ConsumptionRecord, int64, error)
	GetByMemberID(memberID uint, startDate, endDate *time.Time, page, pageSize int) ([]model.ConsumptionRecord, int64, error)
	GetRecentByCardID(cardID uint, limit int) ([]model.ConsumptionRecord, error)
	GetTotalAmount(cardID uint, startDate, endDate *time.Time) (float64, error)
	GetAmountByType(cardID uint, startDate, endDate *time.Time) (map[string]float64, error)
	GetCount(cardID uint, startDate, endDate *time.Time) (int64, error)
	GetRecentRecords(cardID uint, limit int) ([]model.ConsumptionRecord, error)
	GetTransactionsByCardIDs(cardIDs []uint, page, pageSize int) ([]model.ConsumptionRecord, int64, error)
	GetTotalAmountAndCount(cardID uint, startDate, endDate *time.Time) (float64, int64, error)
}

type consumptionRecordRepository struct {
	db *gorm.DB
}

func NewConsumptionRecordRepository(db *gorm.DB) ConsumptionRecordRepository {
	return &consumptionRecordRepository{db: db}
}

func (r *consumptionRecordRepository) Create(record *model.ConsumptionRecord) error {
	return r.db.Create(record).Error
}

func (r *consumptionRecordRepository) GetByID(id uint) (*model.ConsumptionRecord, error) {
	var record model.ConsumptionRecord
	err := r.db.First(&record, id).Error
	return &record, err
}

func (r *consumptionRecordRepository) GetByRecordNo(recordNo string) (*model.ConsumptionRecord, error) {
	var record model.ConsumptionRecord
	err := r.db.Where("record_no = ?", recordNo).First(&record).Error
	return &record, err
}

func (r *consumptionRecordRepository) GetByCardID(cardID uint, consumeType *model.ConsumeType, page, pageSize int) ([]model.ConsumptionRecord, int64, error) {
	var records []model.ConsumptionRecord
	var total int64

	query := r.db.Model(&model.ConsumptionRecord{}).Where("card_id = ?", cardID).Preload("Operator")

	if consumeType != nil {
		query = query.Where("consume_type = ?", *consumeType)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

func (r *consumptionRecordRepository) GetByMemberID(memberID uint, startDate, endDate *time.Time, page, pageSize int) ([]model.ConsumptionRecord, int64, error) {
	var records []model.ConsumptionRecord
	var total int64

	query := r.db.Model(&model.ConsumptionRecord{}).Where("member_id = ?", memberID)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

func (r *consumptionRecordRepository) GetRecentByCardID(cardID uint, limit int) ([]model.ConsumptionRecord, error) {
	var records []model.ConsumptionRecord
	err := r.db.Where("card_id = ?", cardID).
		Order("created_at DESC").Limit(limit).Find(&records).Error
	return records, err
}

func (r *consumptionRecordRepository) GetTotalAmount(cardID uint, startDate, endDate *time.Time) (float64, error) {
	var total float64
	query := r.db.Model(&model.ConsumptionRecord{}).Where("card_id = ?", cardID)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	err := query.Select("COALESCE(SUM(amount), 0)").Scan(&total).Error
	return total, err
}

func (r *consumptionRecordRepository) GetAmountByType(cardID uint, startDate, endDate *time.Time) (map[string]float64, error) {
	type Result struct {
		ConsumeType string
		Total       float64
	}

	var results []Result
	query := r.db.Model(&model.ConsumptionRecord{}).
		Select("consume_type, SUM(amount) as total").
		Where("card_id = ?", cardID).
		Group("consume_type")

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	err := query.Scan(&results).Error
	if err != nil {
		return nil, err
	}

	resultMap := make(map[string]float64)
	for _, r := range results {
		resultMap[r.ConsumeType] = r.Total
	}

	return resultMap, nil
}

func (r *consumptionRecordRepository) GetCount(cardID uint, startDate, endDate *time.Time) (int64, error) {
	var count int64
	query := r.db.Model(&model.ConsumptionRecord{}).Where("card_id = ?", cardID)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	err := query.Count(&count).Error
	return count, err
}

func (r *consumptionRecordRepository) GetRecentRecords(cardID uint, limit int) ([]model.ConsumptionRecord, error) {
	var records []model.ConsumptionRecord
	err := r.db.Where("card_id = ?", cardID).
		Order("created_at DESC").Limit(limit).Find(&records).Error
	return records, err
}

func (r *consumptionRecordRepository) GetTransactionsByCardIDs(cardIDs []uint, page, pageSize int) ([]model.ConsumptionRecord, int64, error) {
	var records []model.ConsumptionRecord
	var total int64

	query := r.db.Model(&model.ConsumptionRecord{}).
		Where("card_id IN ?", cardIDs).
		Preload("Operator")

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
	return records, total, err
}

func (r *consumptionRecordRepository) GetTotalAmountAndCount(cardID uint, startDate, endDate *time.Time) (float64, int64, error) {
	var total float64
	var count int64

	query := r.db.Model(&model.ConsumptionRecord{}).Where("card_id = ?", cardID)

	if startDate != nil {
		query = query.Where("created_at >= ?", startDate)
	}
	if endDate != nil {
		query = query.Where("created_at <= ?", endDate)
	}

	err := query.Select("COALESCE(SUM(amount), 0)").Scan(&total).Error
	if err != nil {
		return 0, 0, err
	}

	err = query.Count(&count).Error
	if err != nil {
		return 0, 0, err
	}

	return total, count, nil
}

// ==================== 余额日志仓储 ====================

type BalanceLogRepository interface {
	Create(log *model.BalanceLog) error
	GetByCardID(cardID uint, page, pageSize int) ([]model.BalanceLog, int64, error)
	GetByMemberID(memberID uint, page, pageSize int) ([]model.BalanceLog, int64, error)
}

type balanceLogRepository struct {
	db *gorm.DB
}

func NewBalanceLogRepository(db *gorm.DB) BalanceLogRepository {
	return &balanceLogRepository{db: db}
}

func (r *balanceLogRepository) Create(log *model.BalanceLog) error {
	return r.db.Create(log).Error
}

func (r *balanceLogRepository) GetByCardID(cardID uint, page, pageSize int) ([]model.BalanceLog, int64, error) {
	var logs []model.BalanceLog
	var total int64

	query := r.db.Model(&model.BalanceLog{}).Where("card_id = ?", cardID).Preload("Operator")

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&logs).Error
	return logs, total, err
}

func (r *balanceLogRepository) GetByMemberID(memberID uint, page, pageSize int) ([]model.BalanceLog, int64, error) {
	var logs []model.BalanceLog
	var total int64

	query := r.db.Model(&model.BalanceLog{}).Where("member_id = ?", memberID).Preload("Operator")

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&logs).Error
	return logs, total, err
}
